package cn.tedu.luban.order.main.service;

import cn.tedu.luban.attach.api.AttachApi;
import cn.tedu.luban.attach.protocol.param.AttachUpdateParam;
import cn.tedu.luban.demand.api.DemandApi;
import cn.tedu.luban.order.common.enums.OrderStatusEnum;
import cn.tedu.luban.order.main.converter.OrderLogConverter;
import cn.tedu.luban.order.main.repository.OrderLogRepository;
import cn.tedu.luban.order.main.repository.OrderRepository;
import cn.tedu.luban.order.protocol.bo.OrderBO;
import cn.tedu.luban.order.protocol.bo.OrderLogBO;
import cn.tedu.luban.order.protocol.param.*;
import cn.tedu.luban.order.protocol.query.FrontOrderListQuery;
import cn.tedu.luban.passport.protocol.jwt.UserPrinciple;
import cn.tedu.luban.passport.sdk.security.LubanSecurityContext;
import cn.tedu.luban.worker.common.enums.WorkerResultEnum;
import com.tedu.inn.protocol.ListRecordTotalBO;
import com.tedu.inn.protocol.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

@Service
@Slf4j
public class OrderService {
    @Autowired
    private DemandApi demandApi;
    @Autowired
    private OrderRepository orderRepository;//order表格
    @Autowired
    private OrderLogRepository orderLogRepository;//order_log表格
    @Autowired
    private OrderLogConverter orderLogConverter;
    private static final Map<Integer,List<Integer>> STATUS_REGISTRY=new HashMap<>();
    static {
        //根据当前业务需求在register添加 状态数据 10-->[20,-30] 20-->[50] 50-->[50,30] 30-->[40]
        List<Integer> targets01=new ArrayList<>();
        targets01.add(20);
        targets01.add(-30);
        STATUS_REGISTRY.put(10,targets01);
        List<Integer> targets02=new ArrayList<>();
        targets02.add(50);
        STATUS_REGISTRY.put(20,targets02);
        List<Integer> targets03=new ArrayList<>();
        targets03.add(50);
        targets03.add(30);
        STATUS_REGISTRY.put(50,targets03);
        List<Integer> targets04=new ArrayList<>();
        targets03.add(40);
        STATUS_REGISTRY.put(30,targets04);
    }
    @Transactional(rollbackFor = Exception.class)
    public String orderCreate(OrderCreateParam param) throws BusinessException {
        //1. 远程调用demand 抢单
        String requestOrderNo = param.getRequestOrderNo();
        Boolean ok = demandApi.grabOrder(requestOrderNo);
        if (!ok){
            //抢单 失败 异常处理
            throw new BusinessException("5001","抢单失败");
        }
        //2. 新增 订单  新增 订单追踪日志
        try{
            //2 新增订单 补充好订单参数
            putOrderNoAndStatus(param);
            orderRepository.saveOrder(param);
            //2 新增日志记录 userId 师傅id orderNo OrderStatusEnum
            OrderLogParam orderLogParam=orderLogConverter
                    .assembleOrderLogParam(param.getUserId(),param.getOrderNo(),OrderStatusEnum.ORDER_CREATE_SUCCESS);
            orderLogRepository.saveOrderLog(orderLogParam);
        }catch (Exception e){
            //对新增订单 和新增订单日志记录 做异常捕获 本地事务失败了
            //TODO 3.归还订单
            demandApi.returnOrder(requestOrderNo);
            throw new BusinessException("5002","新增订单失败");
        }
        //TODO 4. 发送延迟消息
        return param.getOrderNo();
    }

    private void putOrderNoAndStatus(OrderCreateParam param) {
        //1. orderNo uuid 补充 唯一值
        String orderNo= UUID.randomUUID().toString();
        param.setOrderNo(orderNo);
        //2. order 状态. -30[超时关闭] -20[平台取消] -10[用户取消] 10[抢单成功] 20[签到] 50[开始施工] 30[完成待结算] 40[完成]
        param.setStatus(OrderStatusEnum.ORDER_CREATE_SUCCESS.getStatus());
    }

    public ListRecordTotalBO<OrderBO> orderList(FrontOrderListQuery query) {
        //1. 查询total
        Long total = orderRepository.countOrders(query);
        //判断是否为0
        ListRecordTotalBO listBO=null;
        if (total==0){
            listBO=new ListRecordTotalBO(null,0L);
        }else{
            //2.查询分页数据
            List<OrderBO> orderBOS = orderRepository.queryOrders(query);
            listBO=new ListRecordTotalBO(orderBOS,total);
        }
        return listBO;
    }

    public OrderBO orderDetail(String orderNo) throws BusinessException {
        //1. 检查订单是否存在 存在则返回订单数据 不存在则抛异常
        OrderBO orderBO=checkEmpty(orderNo);
        //2. 给orderBO封装 signTime签到时间 来自于orderLog
        List<OrderLogBO> orderLogs = orderLogRepository.getOrderLogByOrderNo(orderNo);
        if (CollectionUtils.isEmpty(orderLogs)){
            throw new BusinessException("6002","订单日志数据为空,请联系管理员");
        }
        //筛选 从orderLogs数组中 拿到签到操作的时间 如果没有返回null
        Long signTime=null;
        for (OrderLogBO orderLog : orderLogs) {
            //不可能所有的日志都是签到 只有operat_name="签到" 拿到operate_time
            String operateName = orderLog.getOperateName();//生成的时候 用的就是订单状态枚举的
            if ("签到".equals(operateName)){
                signTime=orderLog.getOperateTime();
            }
        }
        orderBO.setSignTime(signTime);
        //TODO 3. 封装图片信息
        putAttachUrls(orderBO);
        return orderBO;
    }

    private void putAttachUrls(OrderBO orderBO) {
        //使用订单的数据 查询绑定的图片信息
    }

    private OrderBO checkEmpty(String orderNo) throws BusinessException {
        //1. 订单不一定存在,需要使用订单编号 先查询orderBO
        OrderBO orderBO = orderRepository.getOrderByOrderNo(orderNo);
        //检查订单是否存在 逻辑是可以复用的
        if(orderBO==null||orderBO.getId()==null){
            throw new BusinessException("6001","操作的订单不存在");
        }
        return orderBO;
    }
    @Transactional(rollbackFor = Exception.class)
    public void orderSign(OrderSignParam param) throws BusinessException {
        String orderNo=param.getOrderNo();
        //1.验证参数对应的订单是否是空
        OrderBO orderBO = checkEmpty(orderNo);
        //2.检查定位是否允许签到
        checkLocations();
        //3.检查流转状态是否合法
        Integer currentStatus=orderBO.getStatus();
        Integer targetStatus=OrderStatusEnum.ORDER_SIGN_SUCCESS.getStatus();
        checkStatusTransfer(currentStatus,targetStatus);
        //4.修改订单状态 update order set status=#{targetStatus} where order_no=#{orderNo} and status=10
        OrderParam orderParam=new OrderParam();
        orderParam.setOrderNo(orderNo);
        orderParam.setStatus(targetStatus);
        Long result=orderRepository.updateSignStatus(orderParam);
        if (result==0){
            //乐观锁条件status=10没有满足,说明有别的线程和当前师傅在操作同一个订单
            throw new BusinessException("6003","签到失败,已有订单其它状态占用");
        }
        //5.记录日志信息
        Long userId=getUserId();
        OrderLogParam orderLogParam = orderLogConverter.assembleOrderLogParam(userId, orderNo, OrderStatusEnum.ORDER_SIGN_SUCCESS);
        orderLogRepository.saveOrderLog(orderLogParam);
    }
    private Long getUserId() throws BusinessException {
        UserPrinciple user = LubanSecurityContext.getLoginToken();
        //不一定能拿到登录用户.
        if (user==null){
            throw new BusinessException(WorkerResultEnum.USER_TOKEN_VERIFY_FAILED);
        }
        return user.getId();
    }

    private void checkStatusTransfer(Integer currentStatus, Integer targetStatus)throws BusinessException {
        //必须同时满足2个条件,检查才正确 key值包含currentStatus,并且value值包含targetStatus
        if (STATUS_REGISTRY.containsKey(currentStatus)&&STATUS_REGISTRY.get(currentStatus).contains(targetStatus)){
            return;
        }
        throw new BusinessException("7001","订单流转状态错误");
    }

    private void checkLocations() {
        log.info("开始检查师傅定位是否接近用户地址");
    }
    @Transactional(rollbackFor = Exception.class)
    public void orderConfirm(OrderConfirmParam param) throws BusinessException {
        //1.检查参数ids值是否是空 是空异常结束
        checkEmptyPictures(param);
        //2.检查orderNo
        OrderBO orderBO = checkEmpty(param.getOrderNo());
        //3.检查订单流转状态
        Integer current=orderBO.getStatus();
        Integer target=50;
        checkStatusTransfer(current,target);
        //判断订单当前状态 !=50的时候肯定等于20
        if (current!=50){
            //4. 更新订单状态 update order set status=50 where order_no=? and status=20
            OrderParam orderParam=new OrderParam();
            orderParam.setOrderNo(param.getOrderNo());
            orderParam.setStatus(50);
            Long result=orderRepository.updateConfirmStatus(orderParam);
            if (result==0){
                throw new BusinessException("7002","订单状态修改失败,绑定整体失败");
            }
        }
        //5. 记录施工确认上传图片的日志 userId 师傅 orderNo 订单 记录的订单状态确认施工
        OrderLogParam orderLogParam = orderLogConverter.assembleOrderLogParam(getUserId(), param.getOrderNo(), OrderStatusEnum.ORDER_ATTACH_CONFIRM);
        orderLogRepository.saveOrderLog(orderLogParam);
        //6. 远程调用绑定图片
        bindPictures(param,200,orderBO.getId().intValue());
    }
    @Autowired
    private AttachApi attachApi;
    private void bindPictures
            (OrderConfirmParam param,Integer bizType,Integer bizId) throws BusinessException {
        //从入参解析 图片ids
        List<Long> attachIds = param.getAttachIds();
        List<AttachUpdateParam> attachUpdateParams=new ArrayList<>();
        for (Long attachId : attachIds) {
            //提供图片id bizType bizId isCover非空
            AttachUpdateParam updateParam=new AttachUpdateParam();
            updateParam.setId(attachId.intValue());
            updateParam.setBusinessType(bizType);
            updateParam.setBusinessId(bizId);
            updateParam.setIsCover(1);
            attachUpdateParams.add(updateParam);
        }
        int result = attachApi.batchUpdateAttachByIdList(attachUpdateParams);
        if (result==0){
            throw new BusinessException("50001","图片绑定失败");
        }
    }

    private void checkEmptyPictures(OrderConfirmParam param) throws BusinessException {
        List<Long> attachIds = param.getAttachIds();
        if (CollectionUtils.isEmpty(attachIds)){
            //没办法绑定 没有上传任何图片数据
            throw new BusinessException("10001","图片数据为空,无法绑定");
        }
    }

}
